package com.jacobson.core.widget.security.mgt

import com.jacobson.core.widget.logger.LoggerFactory
import com.jacobson.core.widget.security.*
import com.jacobson.core.widget.security.authc.Authenticator
import com.jacobson.core.widget.security.authc.AuthenticationInfo
import com.jacobson.core.widget.security.authc.AuthenticationToken
import com.jacobson.core.widget.security.exception.AuthenticationException
import com.jacobson.core.widget.security.pam.RealmAuthenticator
import com.jacobson.core.widget.security.realm.Realm

/**
 * ================================================
 *
 *  @author Wucz on 2018/8/6 15:37
 * ================================================
 */

class DefaultSecurityManager(
        realm: Realm? = null
) : SecurityManager {
    private val logger = LoggerFactory.getLogger()
    private var authenticator: Authenticator = RealmAuthenticator()
    private var subjectFactory: SubjectFactory = DefaultSubjectFactory()
    private var realms: MutableList<Realm> = arrayListOf()

    init {
        setRealms(realm?.let { listOf(realm) })
    }

    override fun getSecurityRouter(): SecurityRouter {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    override fun login(subject: Subject, token: AuthenticationToken): Subject {
        try {
            val info = authenticate(token)
            val subjectLogged = createSubject(token, info, subject)
            onSuccessfulLogin(token, info, subjectLogged)
            return subjectLogged
        } catch (e: AuthenticationException) {
            //通知登录失败，删除持久化，缓存等操作
            onFailedLogin(token, e, subject)
            throw e
        }
    }

    private fun createSubject(token: AuthenticationToken, info: AuthenticationInfo, existing: Subject?): Subject {
        val context = this.createSubjectContext()
        context.authenticated = true
        context.authenticationToken = token
        context.authenticationInfo = info
        existing?.let { context.subject = existing }
        return this.createSubject(context)
    }

    private fun createSubjectContext(): SubjectContext = DefaultSubjectContext()

    override fun logout(subject: Subject) {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    override fun createSubject(context: SubjectContext?): Subject {
        var subjectContext = this.copy(context)
        subjectContext = this.ensureSecurityManager(subjectContext)
        subjectContext = this.resolveSession(subjectContext)
        subjectContext = this.resolvePrincipals(subjectContext)
        val subject = this.doCreateSubject(subjectContext)
        this.save(subject)
        return subject
    }

    protected fun save(subject: Subject) {
        SecurityStorage.authenticated = subject.authenticated
        SecurityStorage.principal = subject.principal
    }

    private fun doCreateSubject(context: SubjectContext): Subject {
        return subjectFactory.createSubject(context)
    }

    private fun copy(subjectContext: SubjectContext?): SubjectContext {
        return DefaultSubjectContext(subjectContext)
    }

    private fun ensureSecurityManager(context: SubjectContext): SubjectContext {
        context.resolveSecurityManager()?.let {
            logger.i("Context already contains a SecurityManager instance.  Returning.")
        } ?: let {
            logger.i("No SecurityManager found in context.  Adding self reference.")
            context.securityManager = this
        }
        return context
    }

    private fun resolveSession(context: SubjectContext): SubjectContext {
        context.resolveSession() ?: let {
            if (!context.resolveAuthenticated()) {
                context.authenticated = SecurityStorage.authenticated
            }
            context.resolvePrincipal() ?: run {
                context.principal = SecurityStorage.principal
            }
        }
        return context
    }

    private fun resolvePrincipals(context: SubjectContext): SubjectContext {
        context.resolvePrincipal() ?: let {
            logger.i("No identity (PrincipalCollection) found in the context.  Looking for a remembered identity.")
            getRememberedIdentity(context)?.run {
                logger.d("Found remembered PrincipalCollection.  Adding to the context to be used for subject construction by the SubjectFactory.")
                context.principal = this
            } ?: apply { logger.i("No remembered identity found.  Returning original context.") }
        }
        return context
    }

    private fun getRememberedIdentity(context: SubjectContext): Any? {
//        TODO 获取存储在文件里面的Principal对象
//        val rmm = this.rememberMe
//        if (rmm != null) {
//            try {
//                return rmm!!.getRememberedPrincipal(context)
//            } catch (var5: Exception) {
//                val msg = "Delegate RememberMeManager instance of type [" + rmm!!.getClass().getName() + "] threw an exception during getRememberedPrincipals()."
//                logger.warn(msg, var5)
//            }
//
//        }

        return null
    }

    override fun authenticate(token: AuthenticationToken): AuthenticationInfo = authenticator.authenticate(token)

    protected fun onFailedLogin(token: AuthenticationToken, ae: AuthenticationException, subject: Subject) {
//        this.rememberMeFailedLogin(token, ae, subject)
    }

    private fun onSuccessfulLogin(token: AuthenticationToken, info: AuthenticationInfo, subject: Subject) {
//        this.rememberMeSuccessfulLogin(token, info, subject)
    }


    private fun setRealms(realms: List<Realm>?) {
        if (realms == null) {
            throw IllegalArgumentException("Realm argument cannot be null")
        }
        this.realms.addAll(realms)
        this.afterRealmsSet()
    }

    private fun afterRealmsSet() {
        (authenticator as? RealmAuthenticator)?.setRealms(realms)
    }
}